package com.wan.data.resource;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.wan.core.strategy.StrategyPool;
import com.wan.core.timer.WTimer;
import com.wan.core.timer.rule.WRules;
import com.wan.data.resource.model.IModel;
import com.wan.data.resource.model.ModelCache;
import com.wan.data.resource.model.ResourceModel;
import com.wan.data.resource.parser.ResourceParser;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wwh
 * @date 2022/7/5 16:08
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public class ModelManager {
    private static final Logger logger = LoggerFactory.getLogger(ModelManager.class);
    private static final Map<Class<? extends IModel<?>>, ModelCache<?,? extends IModel<?>>> MODEL_CACHE_MAP = new HashMap<>();
    private static final Map<String,Class<?>> modelNameMap = new HashMap<>();
    private static FileAlterationObserver fileObserver;

    /**
     * 加载资源配置model
     * @param resourceRootPath 配置所在目录的路径
     * @param modelScanPackage 扫描model类的包路径
     */
    public static synchronized void load(String resourceRootPath,String modelScanPackage) throws Exception{
        File root = FileUtil.file(resourceRootPath);
        if(!root.exists()){
            throw new RuntimeException("model配置目录不存在:" + root.getPath());
        }
        List<File> files = FileUtil.loopFiles(root, pathname -> !pathname.getName().startsWith("~$"));
        Set<Class<?>> classSet = ClassScanner.scanPackageBySuper(modelScanPackage, IModel.class);
        Map<String,Class<?>> collect = classSet.stream().collect(Collectors.toMap(cls -> {
            ResourceModel resourceModel = cls.getAnnotation(ResourceModel.class);
            return resourceModel != null && !resourceModel.name().isEmpty() ? resourceModel.name() : StrUtil.lowerFirst(cls.getSimpleName());
        }, cls -> cls));
        modelNameMap.putAll(collect);
        //目录下所有文件中，找到对应的Model映射
        for (File file : files) {
            String load = load(file);
            collect.remove(load);
        }
        collect.keySet().forEach(name->logger.warn("缺少model[{}]对应的配置表",name));
        logger.info("--Model配置表全部加载完毕--");
        //启动热更
        initHotSwap(root);
    }

    /**
     * 解析文件到model
     */
    private static String load(File file) throws Exception{
        String[] split = file.getName().split("[.]");
        if(split.length != 2){
            logger.error("model文件格式有误->{}",file);
            return "";
        }
        String name = split[0];
        String suffix = split[1];
        Class<? extends IModel<?>> modelCls = (Class<? extends IModel<?>>) modelNameMap.get(name);
        if(modelCls == null){
            logger.error("不存在对应model->{}",name);
            return name;
        }
        TimeInterval timer = DateUtil.timer();
        //通过文件后缀名获取解析器
        ResourceParser parser = StrategyPool.getStrategy(ResourceParser.class, suffix);
        List<? extends IModel<?>> models = parser.parse(file, modelCls);
        ModelCache modelCache = MODEL_CACHE_MAP.computeIfAbsent(modelCls, cls -> {
            ResourceModel resourceModel = cls.getAnnotation(ResourceModel.class);
            if (resourceModel == null) resourceModel = ResourceModel.DEFAULT;
            return ReflectUtil.newInstance(resourceModel.cache());
        });
        modelCache.init(models);
        logger.info("加载配置表成功（{}行，耗时{}ms） -> {} ",models.size(),timer.interval(),file.getName());
        return name;
    }

    /**
     * 初始化热更新
     */
    private static synchronized void initHotSwap(File root) throws Exception {
        if(fileObserver != null){
            return;
        }
        fileObserver = new FileAlterationObserver(root);
        fileObserver.addListener(new FileAlterationListenerAdaptor(){
            @Override
            public void onFileCreate(File file) {
                change(file);
            }

            @Override
            public void onFileChange(File file) {
                change(file);
            }

            public void change(File file){
                try {
                    logger.info("热更新model配置 -> {}",file.getName());
                    load(file);
                } catch (Exception exception) {
                    logger.error("热更新model配置出错->{}\n",file,exception);
                }
            }
        });
        fileObserver.initialize();
        //使用时间轮，之所以不使用apache的monitor，是因为它是使用sleep循环检测，增加CPU的线程状态切换，如果很频繁会造成巨大的性能消耗
        WTimer.get().repeat(node->fileObserver.checkAndNotify(), WRules.SECOND_REPEAT,30, LocalDateTime.now().plusMinutes(2));
    }

    /**
     * 获取配置缓存
     */
    public static <ID,M extends IModel<ID>,Cache extends ModelCache<ID,M>> Cache getModelCache(Class<M> modelClass){
        return (Cache) MODEL_CACHE_MAP.get(modelClass);
    }

    /**
     * 获取model的单行数据
     */
    public static <ID,M extends IModel<ID>> M getModel(Class<M> modelClass,ID id){
        return getModelCache(modelClass).get(id);
    }

    /**
     * 获取model的全部数据
     */
    public static <ID,M extends IModel<ID>> List<M> getAllModel(Class<M> modelClass){
        return getModelCache(modelClass).getAll();
    }



}
